package com.cosmoplat.mom.iot.client.http;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.ToString;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.web.util.UriTemplate;

import java.util.*;

/**
 * @author yotta
 * @date 2025/4/15 18:10
 */
@Getter
@ToString
public class HttpRequest {

    private static final String PARAM_SEPARATOR = "&";
    private static final char QUERY_STRING_SEPARATOR = '?';

    private final String url;

    private final String method;

    private Object body;

    private String contentType;

    private final Map<String, String> headers = new HashMap<>();

    private final List<Parameter> queryParams = new ArrayList<>();

    private final List<Parameter> formParams = new ArrayList<>();

    private final List<Parameter> pathParams = new ArrayList<>();

    private static final ObjectMapper MAPPER = new ObjectMapper();

    static {
       // MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
    }

    public static HttpRequest of(String method,String url) {
        return new HttpRequest(url, method);
    }

    public static HttpRequest ofGet(String url) {
        return new HttpRequest(url, "GET");
    }

    public static HttpRequest ofPost(String url) {
        return new HttpRequest(url, "POST");
    }

    public static HttpRequest ofPut(String url) {
        return new HttpRequest(url, "PUT");
    }

    public static HttpRequest ofDelete(String url) {
        return new HttpRequest(url, "DELETE");
    }


    public HttpRequest(String url, String method) {
        this.url = url;
        this.method = method;
        this.contentType(BodyType.JSON);
    }

    public HttpRequest body(Object body) {
        this.body = body;
        return this;
    }

    public HttpRequest addHeader(String key, String value) {
        headers.put(key, value);
        return this;
    }

    public HttpRequest addHeader(Map<String,String> headers) {
        this.headers.putAll(headers);
        return this;
    }


    public HttpRequest addQueryParam(String name, String value) {
        queryParams.add(new Parameter(name, value));
        return this;
    }

    public HttpRequest addPathParam(String name, String value) {
        pathParams.add(new Parameter(name, value));
        return this;
    }


    public HttpRequest contentType(BodyType bodyType) {
        this.contentType = bodyType.getContentType();
        bodyType.addHeader(this);
        return this;
    }

    public HttpRequest contentType(String contentType) {
        this.contentType = contentType;
        addHeader("Content-Type", contentType);
        return this;
    }

    public HttpRequest addQueryParam(Object o) {
        Map<String, Object> map = MAPPER.convertValue(o, new TypeReference<Map<String, Object>>() {});
        return addQueryParam(map);
    }
    public HttpRequest addQueryParam(Map<String, Object> map ) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (Objects.isNull(entry.getValue())) {
                continue;
            }
            queryParams.add(new Parameter(entry.getKey(), String.valueOf(entry.getValue())));
        }
        return this;
    }

    public HttpRequest addFormParam(String name, String value) {
        formParams.add(new Parameter(name, value));
        return this;
    }

    public HttpRequest addFormParam(Object o) {
        Map<String, String> map = MAPPER.convertValue(o, new TypeReference<Map<String, String>>() {});
        map.forEach((key, value) -> addFormParam(key, String.valueOf(value)));
        return this;
    }


    public String completeUrl() {
        String url = replacePathParameters();
        return appendQueryParamToUrl(url);
    }

    private String replacePathParameters() {
        if (CollectionUtils.isEmpty(pathParams)) {
            return url;
        }
        UriTemplate template = new UriTemplate(url);
        Map<String, String> uriParams =  Maps.newHashMap();
        for (Parameter pathParam : pathParams) {
            uriParams.put(pathParam.getName(), pathParam.getValue());
        }
        return template.expand(uriParams).toString();
    }



    private String appendQueryParamToUrl(String url) {
        if (CollectionUtils.isEmpty(queryParams)) {
            return url;
        }
        StringBuilder builder = new StringBuilder(url);
        if (CollectionUtils.isNotEmpty(queryParams)) {
            builder.append(QUERY_STRING_SEPARATOR);
            builder.append(joinQueryParams());
        }
        return builder.toString();
    }

    private String joinQueryParams() {
        StringBuilder builder = new StringBuilder();
        for (Parameter queryParam : queryParams) {
            builder.append(PARAM_SEPARATOR).append(queryParam.asUrlEncodedPair());
        }
        return builder.substring(1);
    }

    public void traverseJsonNode(JsonNode node, int depth) {
        if (node.isObject()) {
            Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> field = fields.next();
                String fieldName = field.getKey();
                JsonNode fieldValue = field.getValue();
                System.out.println(fieldName + ":");
                traverseJsonNode(fieldValue, depth + 1);
            }
        } else if (node.isArray()) {
            for (int i = 0; i < node.size(); i++) {
                JsonNode arrayElement = node.get(i);
                traverseJsonNode(arrayElement, depth + 1);
            }
        } else {
            addQueryParam(node.fieldNames().next(), node.asText());
            System.out.println(node.asText());
        }
    }

    private static void printIndent(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("  ");
        }
    }

}
